Repository navigation
Conversation
Coverage
|
pcarrier
force-pushed
the
receive-budget
branch
2 times, most recently
from
September 30, 2026 09:22
b1f381c to
5f62df0
Compare
…stalls over WebSocket (yas-run#84) * Show that uplink WebSocket streams send small writes without Nagle stalls A producer's stream WebSockets turn Nagle's algorithm off, so an echo written a moment after another byte goes at once instead of waiting for the relay's delayed acknowledgement. The new test answers each consumer byte with two a millisecond apart: with Nagle on, every round takes about 41 ms; with it off, well under 20. * Start uplink WebTransport sessions with a window for a whole answer quinn paces a congestion window over the round trip (1.25 windows per RTT), and an uplink's answers leave its connection app-limited, which keeps CUBIC's window from growing past them. From quinn's 14,720 bytes, a 1 MiB answer settled at two round trips: 200 ms at 100 ms RTT, where the same read over TCP (ssh, or the WebSocket carrier) takes one. Sessions now start with a 16 MiB window, and the uplink asks for an 8 MiB UDP receive buffer (Linux's default of 208 KiB overflows under a paced burst of up to 256 datagrams). Loss still shrinks the window, and a stream's 1.25 MB receive window still bounds what one consumer has in flight. Measured through a relay doing the same (read_file 1 MiB, p50): 100 ms RTT 200 -> 114 ms, 200 ms RTT 411 -> 222 ms. * Say what UDP receive buffer an uplink WebTransport session got The uplink asks for an 8 MiB UDP receive buffer, and a system may grant less (Linux caps it at net.core.rmem_max, then doubles it). That never fails a session, but it makes bursts lose packets, so the producer now reports the size it got as it connects, a new `Event::ReceiveBuffer`: `yas uplink` prints "UDP receive buffer: N bytes", and when N is under what it asked for, says the system caps it. * Fit the uplink's receive buffer to what macOS, the BSDs and Linux allow Three issues from the review of yas-run#84: - macOS and the BSDs refuse a UDP receive buffer over their cap (kern.ipc.maxsockbuf less mbuf overhead: 7,456,540 bytes of macOS's usual 8 MiB) with ENOBUFS rather than capping it, so asking for 8 MiB left the socket at its default, and the event blamed a cap. A refused ask now looks for the largest size the system takes, between its default and 8 MiB (23 tries at most). - Linux reports double what it allows, so 8 MiB asked for reads as 16 MiB, but the event compared that with 8 MiB: a net.core.rmem_max from 4 MiB up to 8 MiB was never noted (4 MiB, as here, reads as 8388608). The event now compares with what all of it reports (16 MiB on Linux and Android) and says so: "under the 16777216 Linux reports for the 8388608 asked for (net.core.rmem_max caps it)", naming kern.ipc.maxsockbuf on macOS and FreeBSD. A test checks a real socket against the machine's rmem_max, and fails with the old comparison here. - quinn's initial window is 12,000 bytes (14,720 clamped to ten 1,200-byte datagrams), not 14,720: fixed in the comment and docs/uplink.md.
…as-run#88) * Keep a command's output head and tail, drop its middle at pipe speed SPAWN_KEEP_OUTPUT (32), with REPORT_EXIT and SPAWN extension tag 4 [head_bytes, tail_bytes] (the tail at most 1 MiB), sends only the head and the tail of each output stream. What comes between is dropped as the server reads it, never held for the client's credit, so a command writing far more than its client keeps runs at the speed of its pipe instead of one window per round trip. The cuts fall between characters as a WHATWG UTF-8 decoder with replacement reads the whole stream (a JavaScript TextDecoder, Rust's from_utf8_lossy), so decoding the head and the tail gives exactly the characters they have within it. The EXIT event says what was dropped of each stream (extension tags 1 and 2, OutputElision: offset, bytes, lines, code points, UTF-16 units), so a client can say how much it did not get in the units it counts. The output reader waits for the owner only while the head goes out, keeps the last tail_bytes in a ring, and sends them at the stream's end, or before the exit is reported when the stream outlives it (a residue past its grace, TERMINATE, a lost owner, a forced cleanup): flush_kept. yas-client: Command::keep_output(head, tail), set where the server offers it; Process::elided(stderr) once the exit came. * Keep the elision in Process::output; gate mod process again Review of yas-run#88: Output had no place for what KEEP_OUTPUT dropped, and output() takes the process, so a kept command's head and tail came joined with nothing to say a middle was missing. Output.elided now carries it (the test's helper is output_limited again). And the new mod output_keep line took mod process's #[cfg(any(unix, windows))]. * Check an output elision's UTF-16 bound without overflowing protocol-fuzz found OutputElision::decode multiplying a decoded code point count by two, which panics past u64::MAX / 2. Saturate instead: a count that wide bounds nothing it could hold. Test the five counts' rules, prefixes, and the widest values.
…m it Every stdout and stderr stream holds its window of the session's receive budget while it is open, whether it writes or not, and yas-client divides three quarters of that budget between the server's per-session maximum of processes. The budget was always 16 MiB (RECOMMENDED_BUFFERED): a client that runs 256 processes a session got 24 KiB windows, and over a network a stream carries about a window a round trip (20 MB took 88 s at 100 ms). A wider window per command instead oversubscribes the budget: once it is all held, a new stream gets no credit until another lets go. HelloOptions::receive_budget (16 MiB by default, at most 1 GiB) is the HELLO receive max_buffered this client declares; Client::receive_budget says it, and default_process_window divides it instead of the constant. At 256 processes in 256 MiB each stream gets 384 KiB, and they all fit. Servers already honour a peer's budget above 16 MiB (their outbound credit is the peer's max_buffered). Test (client_host): a_wider_receive_budget_fits_every_process_window: at 256 processes a session, 16 MiB gives 24 KiB windows and 256 MiB 384 KiB; with 255 quiet processes holding theirs (510 streams, 191 MiB), one more gets all of three windows of output at once. With the same windows in 16 MiB, that command gets no credit (checked: it waited past 30 s).
Review of yas-run#87: NativeClient's queue of frames parked while a caller waits for another stayed capped at 16 MiB and 1,024 frames, so a client that declared a wider budget, and was sent within it, failed its session with "native YAS peer exceeded the bounded pending-frame queue". The cap is now the declared budget in bytes, and one frame per 16 KiB of it in frames (at least 1,024, the default's). The new test parks 17 MiB in 1,372 frames within a 64 MiB budget; capping either bound at the default's makes it fail.
pcarrier
force-pushed
the
receive-budget
branch
from
September 30, 2026 10:05
5f62df0 to
a885343
Compare
Author
|
Upstream PR merged into yas-run/yas main (now dd33f04): this CI-only draft is done. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fork CI for the receive-budget PR on yas-run/yas (stacked on yas-run#85). Do not merge.